use std::collections::HashMap;
use std::rc::Rc;
use crate::expr::{Assign, Binary, Call, Expression, ExprTrait, ExprVisitor, Get, Grouping, Literal, Logical, Set, Super, This, Triple, Unary, Variable};
use crate::interpreter::Interpreter;
use crate::scanner::{LiteralValue, Token};
use crate::stmt::{Stmt, StmtBlock, StmtClass, StmtExpr, StmtFunction, StmtIf, StmtPrint, StmtReturn, StmtTrait, StmtVar, StmtVisitor, StmtWhile};

#[derive(Copy, Clone, Eq, PartialEq)]
enum FunctionType {
    NONE,
    FUNCTION,
    INITIALIZER,
    METHOD
}

#[derive(Copy, Clone, Eq, PartialEq)]
enum ClassType {
    NONE,
    CLASS,
    SUBCLASS
}

pub struct Resolver<'a> {
    interpreter: & 'a mut Interpreter,
    scopes: Vec<HashMap<String, bool>>,
    current_function: FunctionType,
    current_class: ClassType
}

impl <'a> Resolver<'a> {
    pub fn new(interpreter: & 'a mut Interpreter) -> Self {
        Self { interpreter, scopes: Vec::new(), current_function: FunctionType::NONE, current_class: ClassType::NONE }
    }
}

/// # 需要处理的语句/表达式
/// - 块语句，创建新的scope
/// - 函数声明，除了块语句的处理外，还需将处理的参数绑定到当前scope
/// - 变量声明，将其加入到当前scope
/// - 变量赋值，需要将变量进行绑定

impl<'a> Resolver<'a> {
    fn begin_scope(&mut self) {
        self.scopes.push(HashMap::new());
    }

    fn end_scope(&mut self) {
        self.scopes.pop();
    }

    fn resolve_stmt(&mut self, stmt: &Stmt) -> ResolveResult {
        stmt.accept(self)
    }

    fn resolve_expr(&mut self, stmt: &Expression) -> ResolveResult {
        stmt.accept(self)
    }

    fn resolve_stmts(&mut self, stmts:&[Stmt]) -> ResolveResult {
        for stmt in stmts {
            stmt.accept(self)?;
        }

        Ok(())
    }

    fn resolve_local(&mut self, expr: &Expression, name: &Token) {
        for (rev_idx, m) in self.scopes.iter().rev().enumerate() {
            if m.contains_key(&name.lexeme) {
                self.interpreter.resolve(expr.clone(),  rev_idx);
                break;
            }
        }
    }

    fn resolve_function(&mut self, stmt: &StmtFunction, func_type: FunctionType) -> ResolveResult {
        let enclosingFunc = self.current_function;
        self.current_function = func_type;
        self.begin_scope();
        for param in stmt.params.iter() {
            self.declare(param);
            self.define(param);
        }
        self.resolve_stmts(&stmt.body)?;
        self.end_scope();
        self.current_function = enclosingFunc;
        Ok(())
    }

    fn declare(&mut self, name: &Token) {
        if let Some(mut m) = self.scopes.last_mut() {
            m.insert(name.lexeme.clone(), false);
        }
    }

    fn define(&mut self, name: &Token) {
        if let Some(mut m) = self.scopes.last_mut() {
            m.insert(name.lexeme.clone(), true);
        }
    }
}

type ResolveResult = Result<(), String>;

impl <'a> ExprVisitor<ResolveResult> for Resolver<'a> {
    fn visit_binary_expr(&mut self, expr: &Binary) -> ResolveResult {
        self.resolve_expr(&expr.left)?;
        self.resolve_expr(&expr.right)
    }

    fn visit_grouping_expr(&mut self, expr: &Grouping) -> ResolveResult {
        self.resolve_expr(&expr.expr)
    }

    fn visit_literal(&self, expr: &Literal) -> ResolveResult {
        Ok(())
    }

    fn visit_unary(&mut self, expr: &Unary) -> ResolveResult {
        Ok(())
    }

    fn visit_triple(&mut self, expr: &Triple) -> ResolveResult {
        self.resolve_expr(&expr.judge_expr)?;
        self.resolve_expr(&expr.true_expr)?;
        self.resolve_expr(&expr.false_expr)
    }

    fn visit_variable(&mut self, expr: &Variable) -> ResolveResult {
        if !self.scopes.is_empty() {
            match self.scopes.last() {
                None => {}
                Some(m) => {
                    match m.get(&expr.token.lexeme) {
                        None => {}
                        Some(b) => {
                            if *b == false {
                                return Err("Can't read local variable in its own initializer.".to_string());
                            }
                        }
                    }
                }
            }
        }

        self.resolve_local(&Expression::Variable(expr.clone()), &expr.token);

        Ok(())
    }

    fn visit_assign(&mut self, expr: &Assign) -> ResolveResult {
        self.resolve_expr(&expr.value)?;
        let e = Expression::Assign(expr.clone());
        self.resolve_local(&e, &expr.name);
        Ok(())
    }

    fn visit_logical(&mut self, expr: &Logical) -> ResolveResult {
        self.resolve_expr(&expr.left)?;
        self.resolve_expr(&expr.right)
    }

    fn visit_call_expr(&mut self, expr: &Call) -> ResolveResult {
        self.resolve_expr(&expr.callee)?;
        for argument in expr.arguments.iter() {
            self.resolve_expr(&argument)?;
        }

        Ok(())
    }

    fn visit_get_expr(&mut self, expr: &Get) -> ResolveResult {
        self.resolve_expr(&expr.object);
        Ok(())
    }

    fn visit_set_expr(&mut self, expr: &Set) -> ResolveResult {
        self.resolve_expr(&expr.object);
        self.resolve_expr(&expr.value);
        Ok(())
    }

    fn visit_this_expr(&mut self, expr: &This) -> ResolveResult {
        if self.current_class != ClassType::CLASS {
            return Err("Can't use 'this' outside of a class.".to_string());
        }
        let e = Expression::This(expr.clone());
        self.resolve_local(&e, &expr.keyword);
        Ok(())
    }

    fn visit_super_expr(&mut self, expr: &Super) -> ResolveResult {
        if self.current_class == ClassType::NONE {
            return Err("Can't use 'super' outside of a class.".to_string())
        } else if self.current_class != ClassType::SUBCLASS {
            return Err("Can't use 'super' in a class with no superclass.".to_string());
        }
        self.resolve_local(&Expression::Super(expr.clone()), &expr.keyword);
        Ok(())
    }
}

impl <'a> StmtVisitor<ResolveResult> for Resolver<'a> {
    fn visit_print_stmt(&mut self, expr: &StmtPrint) -> ResolveResult {
        self.resolve_expr(&expr.expr)
    }

    fn visit_expr_stmt(&mut self, expr: &StmtExpr) -> ResolveResult {
        self.resolve_expr(&expr.expr)
    }

    fn visit_var_stmt(&mut self, stmt: &StmtVar) -> ResolveResult {
        self.declare(&stmt.name);
        self.resolve_expr(&stmt.initializer)?;
        self.define(&stmt.name);
        Ok(())
    }

    fn visit_block_stmt(&mut self, stmt: &StmtBlock) -> ResolveResult {
        self.begin_scope();
        self.resolve_stmts(&stmt.stmts)?;
        self.end_scope();
        Ok(())
    }

    fn visit_if_stmt(&mut self, stmt: &StmtIf) -> ResolveResult {
        self.resolve_expr(&stmt.condition)?;
        self.resolve_stmt(&stmt.then_stmt)?;
        match stmt.else_stmt {
            None => Ok(()),
            Some(ref else_stmt) => self.resolve_stmt(else_stmt)
        }
    }

    fn visit_while_stmt(&mut self, stmt: &StmtWhile) -> ResolveResult {
        self.resolve_expr(&stmt.condition)?;
        self.resolve_stmt(&stmt.body)
    }

    fn visit_break_stmt(&self) -> ResolveResult {
        Ok(())
    }

    fn visit_continue_stmt(&self) -> ResolveResult {
        Ok(())
    }

    fn visit_class_stmt(&mut self, stmt: &StmtClass) -> ResolveResult {
        let enclosing_class = self.current_class;
        self.current_class = ClassType::CLASS;
        self.declare(&stmt.name);
        self.define(&stmt.name);

        if let Some(ref super_cls) = stmt.superclass {
            if super_cls.token.lexeme == stmt.name.lexeme {
                return Err("A class can't inherit from itself.".to_string());
            }
            self.current_class = ClassType::SUBCLASS;
            self.resolve_expr(&Expression::Variable(super_cls.clone()))?;

            self.begin_scope();
            if let Some(m) = self.scopes.last_mut() {
                m.insert("super".to_string(), true);
            }
        }

        self.begin_scope();
        if let Some(mut m) = self.scopes.last_mut() {
            m.insert("this".to_string(), true);
        }
        for f in stmt.methods.iter() {
            let mut declaration = FunctionType::METHOD;
            if f.name.lexeme == "init" {
                declaration = FunctionType::INITIALIZER
            }
            self.resolve_function(f, declaration);
        }

        self.end_scope();

        if stmt.superclass.is_some() {
            self.end_scope();
        }
        self.current_class = enclosing_class;
        Ok(())
    }

    fn visit_function_stmt(&mut self, f: &StmtFunction) -> ResolveResult {
        self.declare(&f.name);
        self.define(&f.name);

        self.resolve_function(f, FunctionType::FUNCTION)
    }

    fn visit_return_stmt(&mut self, stmt: &StmtReturn) -> ResolveResult {
        if self.current_function != FunctionType::FUNCTION {
            return Err("Can't return from top-level code.".to_string())
        } else {
            if let Some(ref expr) = stmt.value {
                if self.current_function == FunctionType::INITIALIZER {
                    return Err("Can't return a value from an initializer.".to_string())
                }
                self.resolve_expr(expr)?;
            }
        }
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use crate::expr::EvaluatedValue;
    use crate::interpreter::{ExecErr, ExecResult, Interpreter};
    use crate::parser::Parser;
    use crate::resolver::Resolver;
    use crate::scanner::Scanner;

    fn exec(s: &str) -> ExecResult {
        let mut scanner = Scanner::new(s);
        let tokens = scanner.scan_tokens().unwrap();
        let mut interpreter = Interpreter::new();
        let mut parser = Parser::new(tokens);
        let stmts = parser.parse().map_err(|e| ExecErr::Other(e))?;
        println!("stmts: {:?}", stmts);
        let mut resolver = Resolver::new(&mut interpreter);
        resolver.resolve_stmts(&stmts).map_err(|e| ExecErr::Other(e))?;
        drop(resolver);
        interpreter.interpret(&stmts)?;

        Ok(EvaluatedValue::nil())
    }

    #[test]
    fn function_definition_test() {
        let s = "fun add(a, b) {\
            return a+b;
        }
        print 1+1;
        ";

        println!("{:?}", exec(s));
    }

    #[test]
    fn function_cascade_call_test() {
        let s = "fun newfun() {\
            fun add(a, b) {
              return a+b;
            }
           return add;
        }
        print newfun()(1,2);
        ";

        println!("{:?}", exec(s));
    }

    #[test]
    fn function_shadow_test() {
        let s = "var a = 1;\
{
        fun pp() {\
            print a;
        }

var a = 4;
       pp();
}
        ";

        println!("{:?}", exec(s));
    }

    #[test]
    fn class_init_test() {
        let s = "class Aaa {\
           bb() {
               print \"xia bb\";
           }
        }
        var i = Aaa();
        print i;
        ";

        println!("{:?}", exec(s));
    }

    #[test]
    fn instance_set_test() {
        let s = "class Cls {\
           b() {
             return \"hii\";
           }
           }
        var c = Cls();
        c.a = \"ooo\";
        print c.a;
        c.a = \"kkk\";
        print c.a;
        ";

        println!("{:?}", exec(s));

    }


    #[test]
    fn instance_call_without_this() {
        let s = "class Bacon {
  eat() {
    print \"Crunch crunch crunch!\";
  }
}

Bacon().eat();";
        exec(s);
    }

    #[test]
    fn call_with_this() {
        let s = "class Cake {
  taste() {
    var adjective = \"delicious\";
    print \"The \" + this.flavor + \" cake is \" + adjective + \"!\";
  }
}

var cake = Cake();
cake.flavor = \"German chocolate\";
cake.taste();";
        println!("{:?}", exec(s));
    }

    #[test]
    fn constructor_test() {
        let s = "class Foo {
  init() {
   print 111;
    this.a = 1;
    print this;
  }
}

var foo = Foo();
print foo.a;";
        println!("{:?}", exec(s));
    }

    #[test]
    fn super_method_resolve() {
        let s= "class Doughnut {
  cook() {
    print \"Fry until golden brown.\";
  }
}

class BostonCream < Doughnut {}

BostonCream().cook();
";
        println!("{:?}", exec(s));
    }

    #[test]
    fn super_ref_test() {
        let s = "class A {
  method() {
    print \"A method\";
  }
}

class B < A {
  method() {
    print \"B method\";
  }

  test() {
    super.method();
  }
}

class C < B {}

C().test();";
        println!("{:?}", exec(s));
    }
}